﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.NetworkInformation;
using System.Collections;
using System.Net;
using System.Net.Sockets;
using System.Threading;

// Network Class v.1.3.1
// by George Makarov
// Email: georgemakarov1@yandex.ru

namespace NetworkLib
{
    public class cNetwork
    {
        private class StateObject
        {
            // Client socket.
            public Socket UdpSocket = null;
            // Size of receive buffer.
            public const int BufferSize = 1024;
            // Receive buffer.
            public byte[] buffer = new byte[BufferSize];
            // Received data string.
            public StringBuilder sb = new StringBuilder();
            public byte[] bReceived = new byte[0];
        }
//         [Serializable]
        private struct stNetwork
        {
            public IPAddress IP;
            public IPAddress Mask;
            public IPAddress Subnet;
            public IPAddress Broadcast;
        }
        public struct stUDPReceiveData
        {
            public IPAddress IP;
            public string Data;
        }
        public struct stTCPReceiveData
        {
            public IPAddress IP;
            public string Data;
            public Socket client;
        }
        public delegate void dUDPReceiveHandler(stUDPReceiveData ReceiveData);
        public event dUDPReceiveHandler UDPReceiveHandler;
        public delegate void dTCPReceiveHandler(stTCPReceiveData ReceiveData);
        public delegate void dTCPErrorHandler(Exception ex);
        public event dTCPReceiveHandler TCPReceiveHandler;
        //public event dTCPErrorHandler TCPErrorHandler;
        public delegate void dErrorHandler(Exception ex);
        public event dErrorHandler ErrorHandler;
        private Socket UDPListener_socket;
        private UdpClient UDPSender;
        private UdpClient UDPListener_udpclient;
        private ArrayList Networks;
        private string ReceiveString;
        //private int iIntPort;
        private int iExtPort;
        private int iIntWaitPort;
        private bool bUDPWaitMessage = false;
        private bool bTCPWaitMessage = false;
        Thread UDPReceiveThread;
        Thread TCPReceiveThread;
        StateObject State;
        TcpListener TCPListener;
        //List<Socket> TCPClients;


        public cNetwork()
        {
            Networks = new ArrayList();
            GetNetworks(ref Networks);
        }

        ~cNetwork()
        {
            try
            {
                UDPListener_socket.Close();
                UDPListener_udpclient.Close();
                TCPListener.Stop();
            }
            catch
            { }
        }
        //public ArrayList GetNetworks()
        //{
        //    return Networks;
        //}

        public bool TCPWaitMessage(int aPort)
        {
            TCPListener = new TcpListener(aPort);
            bTCPWaitMessage = true;
            try
            {
                TCPListener.Start();
            }
            catch (Exception ex)
            {
                if (ErrorHandler != null)
                    ErrorHandler(ex);
                return false;
            }
            TCPReceiveThread = new Thread(TCPReceive);
            TCPReceiveThread.Start();
            return true;
        }

        private void TCPReceive()
        {
            while (bTCPWaitMessage)
            {
                Thread.Sleep(1);
                try
                {
                    if (TCPListener.Pending())
                    {
                        Socket socket = TCPListener.AcceptSocket();
                        byte[] bytes = new byte[16384];
                        int iReceived = socket.Receive(bytes, bytes.Length, 0);
                        stTCPReceiveData ReceiveData = new stTCPReceiveData();
                        ReceiveData.IP = (socket.RemoteEndPoint as IPEndPoint).Address;
                        ReceiveData.Data = Encoding.ASCII.GetString(bytes, 0, iReceived);
                        ReceiveData.client = socket;
                        if (TCPReceiveHandler != null)
                            TCPReceiveHandler(ReceiveData);

                    }
                }
                catch (Exception ex)
                {
                    if (ErrorHandler != null)
                        ErrorHandler(ex);
                }
            }
            TCPListener.Stop();
        }

        public void TCPSendMessage(string aMessage, ref Socket socket)
        {
            TCPSendMessage(Encoding.ASCII.GetBytes(aMessage), ref socket);
        }

        public void TCPSendMessage(Byte[] bSendData, ref Socket socket)
        {
            int numBytes = 0;
            try
            {
                if (socket.Connected) 
                {
                    if ((numBytes = socket.Send(bSendData, bSendData.Length, 0)) == -1)
                        throw new Exception("Ошибка сокета при отправке пакета");
                }
                else
                throw new Exception("Соединение было разорвано");
            }
            catch (Exception ex) 
            {
                if (ErrorHandler != null)
                    ErrorHandler(ex);
                //Console.WriteLine("Error Occurred : {0} ", e); 
            }
        }

        public void UDPSendMessage(string aMessage, IPAddress aAddress, /*int aIntPort,*/ int aExtPort, int aCount)
        {
            //iIntPort = aIntPort;
            iExtPort = aExtPort;
            UDPSendMsg(aMessage, aAddress, false, aCount);
        }

        public void UDPSendMessage(string aMessage, bool aBroadcast, /*int aIntPort,*/ int aExtPort, int aCount)
        {
            //iIntPort = aIntPort;
            iExtPort = aExtPort;
            UDPSendMsg(aMessage, null, true, aCount);
        }

        private void UDPSendMsg(string aMessage, IPAddress aAddress, bool aBroadcast, int aCount)
        {
            for (int i = 0; i < aCount; i++)
            {
                if (Networks.Count != 0)
                {
                    foreach (stNetwork Network in Networks)
                    {
                        try
                        {
                            IPEndPoint EndPoint;
                            if (aBroadcast)
                            {
                                EndPoint = new IPEndPoint(Network.Broadcast, iExtPort);
                            }
                            else
                            {
                                EndPoint = new IPEndPoint(aAddress, iExtPort);
                            }
                            string strMessage = aMessage;
                            byte[] abSenderAddress = Network.IP.GetAddressBytes();
                            byte[] abMessage = System.Text.ASCIIEncoding.ASCII.GetBytes(strMessage);
                            byte[] RequestBytes = new byte[abSenderAddress.Length + abMessage.Length];
                            System.Buffer.BlockCopy(abSenderAddress, 0, RequestBytes, 0, abSenderAddress.Length);
                            System.Buffer.BlockCopy(abMessage, 0, RequestBytes, abSenderAddress.Length, abMessage.Length);
                            UDPSender = new UdpClient();
                            UDPSender.EnableBroadcast = aBroadcast;
                            //for (int i = 0; i < aCount; i++)
                            {
                                UDPSender.Send(RequestBytes, RequestBytes.Length, EndPoint);
                                UDPSender.Send(RequestBytes, 0, EndPoint);
                            }
                            UDPSender.Close();
                        }
                        catch (Exception ex)
                        {
                            if (ErrorHandler != null)
                                ErrorHandler(ex);
                        }
                    }
                }
            }
        }

        private void OnUDPSend(IAsyncResult ar)
        {
            // Retrieve the socket from the state object.
            Socket client = (Socket) ar.AsyncState;
            // Complete sending the data to the remote device.
            int bytesSent = client.EndSend(ar);
        }

        public void UDPWaitMessageAsync(int aPort)
        {
            iIntWaitPort = aPort;
            ReceiveString = String.Empty;
            bUDPWaitMessage = true;

            try
            {
                IPEndPoint EndPoint = new IPEndPoint(IPAddress.Any, iIntWaitPort);
                UDPListener_socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                UDPListener_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
                UDPListener_socket.Bind(EndPoint);

                State = new StateObject();
                State.UdpSocket = UDPListener_socket;
                UDPListener_socket.BeginReceive(State.buffer, 0, StateObject.BufferSize, 0,
                    new AsyncCallback(UDPRecieveAsync), State);
            }
            catch (Exception ex)
            {
                if (ErrorHandler != null)
                    ErrorHandler(ex);
            }
        }

        public void UDPWaitMessage_socket(int aPort)
        {
            iIntWaitPort = aPort;
            
            //UDPListener.BeginReceive(State.buffer, 0, StateObject.BufferSize, 0,
            //    new AsyncCallback(OnUDPRecieve), State);

            bUDPWaitMessage = true;
            UDPReceiveThread = new Thread(UDPReceive_socket);
            UDPReceiveThread.Start();
        }

        public void UDPWaitMessage_udpclient(int aPort)
        {
            iIntWaitPort = aPort;
            bUDPWaitMessage = true;
            UDPReceiveThread = new Thread(UDPReceive_udpclient);
            UDPReceiveThread.Start();
        }

        public void UDPStopWaitMessage()
        {
            bUDPWaitMessage = false;
        }

        public void TCPStopWaitMessage()
        {
            bTCPWaitMessage = false;
        }

        private void UDPReceive_socket()
        {
            while (bUDPWaitMessage)
            {
                Thread.Sleep(1);
                ReceiveString = String.Empty;
                try
                {
                    IPEndPoint EndPoint = new IPEndPoint(IPAddress.Any, iIntWaitPort);
                    UDPListener_socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                    UDPListener_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
                    UDPListener_socket.Bind(EndPoint);

                    State = new StateObject();
                    State.UdpSocket = UDPListener_socket;

                    int bytesRead = UDPListener_socket.Receive(State.buffer);
                    ReceiveString = Encoding.ASCII.GetString(State.buffer, 0, bytesRead);

                    stUDPReceiveData ReceiveData = new stUDPReceiveData();
                    ReceiveData.Data = ReceiveString;
                    if (UDPReceiveHandler != null)
                        UDPReceiveHandler(ReceiveData);
                    UDPListener_socket.Close();
                }
                catch (Exception ex)
                {
                    if (ErrorHandler != null)
                        ErrorHandler(ex);
                }

            }
        }

        private void UDPReceive_udpclient()
        {
            UDPListener_udpclient = new UdpClient(iIntWaitPort);
            IPEndPoint EndPoint = new IPEndPoint(IPAddress.Any, iIntWaitPort);
            while (bUDPWaitMessage)
            {
                //Thread.Sleep(1);
                try
                {
                    byte[] bytes = UDPListener_udpclient.Receive(ref EndPoint);
                    ReceiveString = Encoding.ASCII.GetString(bytes);
                    stUDPReceiveData ReceiveData = new stUDPReceiveData();
                    ReceiveData.Data = ReceiveString;
                    ReceiveData.IP = EndPoint.Address;
                    if (UDPReceiveHandler != null)
                        UDPReceiveHandler(ReceiveData);
                }
                catch (Exception ex)
                {
                    if (ErrorHandler != null)
                        ErrorHandler(ex);
                }
            }
        }
        
        private void UDPRecieveAsync(IAsyncResult ar)
        {
            if (!bUDPWaitMessage)
                return;
            StateObject state = (StateObject)ar.AsyncState;
            Socket client = state.UdpSocket;

            int bytesRead = client.EndReceive(ar);

            if (bytesRead > 0)
            {
                state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));
                byte[] bTmp = new byte[state.bReceived.Length+bytesRead];
                System.Buffer.BlockCopy(state.bReceived, 0, bTmp, 0, state.bReceived.Length);
                System.Buffer.BlockCopy(state.buffer, 0, bTmp, state.bReceived.Length, bytesRead);
                state.bReceived = bTmp;
                client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                    new AsyncCallback(UDPRecieveAsync), state);
            }
            else
            {
                if (state.sb.Length > 0)
                {
                    ReceiveString = state.sb.ToString();
                    stUDPReceiveData ReceiveData = new stUDPReceiveData();
                    byte[] bIP = new byte[4];
                    System.Buffer.BlockCopy(state.bReceived, 0, bIP, 0, 4);
                    ReceiveString = ReceiveString.Remove(0, 4);
                    ReceiveData.IP = new IPAddress(bIP);
                    ReceiveData.Data = ReceiveString;
                    if (UDPReceiveHandler != null)
                        UDPReceiveHandler(ReceiveData);
                    state.UdpSocket.Close();
                    //WaitMessage(iIntWaitPort);
                }
            }
        }

        private void GetNetworks(ref ArrayList aNetworks)
        {
            UInt32 lMask, lIP, lSubnet, lBroadcast;
            IPAddress IP, Mask, Subnet, Broadcast;
            try
            {
                NetworkInterface[] networks = NetworkInterface.GetAllNetworkInterfaces();
                foreach (NetworkInterface network in NetworkInterface.GetAllNetworkInterfaces())
                {
                    IPInterfaceProperties IProps = network.GetIPProperties();
                    foreach (UnicastIPAddressInformation unicastAddress in IProps.UnicastAddresses)
                    {
                        IP = unicastAddress.Address;
                        if (IP.AddressFamily != AddressFamily.InterNetwork)
                            continue;
                        lIP = (UInt32)IP.Address;
                        if (unicastAddress.IPv4Mask == null)
                            continue;
                        else
                        {
                            Mask = new IPAddress(unicastAddress.IPv4Mask.Address);
                            lMask = (UInt32)Mask.Address;
                            lSubnet = lIP & lMask;
                            Subnet = new IPAddress(lSubnet);
                            lBroadcast = (~lMask) | lSubnet;
                            Broadcast = new IPAddress(lBroadcast);
                            stNetwork nw = new stNetwork();
                            nw.IP = IP;
                            nw.Mask = Mask;
                            nw.Subnet = Subnet;
                            nw.Broadcast = Broadcast;
                            aNetworks.Add(nw);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                if (ErrorHandler != null)
                    ErrorHandler(ex);
            }
        }

        private delegate IPHostEntry GetHostEntryHandler(string ip); 
        public static string GetReverseDNS(string ip, int timeout)
        {
            try
            {
                GetHostEntryHandler callback = new GetHostEntryHandler(Dns.GetHostEntry);

                IAsyncResult result = callback.BeginInvoke(ip, null, null);
                if (result.AsyncWaitHandle.WaitOne(timeout, false))
                {
                    return callback.EndInvoke(result).HostName;
                }
                else
                {
                    return ip;
                }
            }
            catch (Exception)
            {
                return ip;
            }
        }
    }
}
